import CanvasComponent, { CanvasComponentOptions } from "./CanvasComponent";

export enum TextAlign {
  left,
  center,
  right
}

type FontWeightVars = 'bold' | 'normal' | 'lighter' | 'bolder' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'
type FontStyleVars = 'normal' | 'italic' | 'oblique'

export type TextOptions = CanvasComponentOptions & {
  width?: number
  height?: number
  lineHeight?: number
  baseline?: CanvasTextBaseline
  textAlign?: TextAlign
  fontSize?: number
  color?: string | CanvasGradient | CanvasPattern
  fontFamily?: string
  fontWeight?: FontWeightVars
  fontStyle?: FontStyleVars
  backgroundColor?: string | CanvasGradient | CanvasPattern
  wrap?: boolean
  wordSpacing?: number
}

export default class Text extends CanvasComponent {
  content: string
  textAlign: TextAlign
  fontSize: number
  lineHeight: number
  baseline: CanvasTextBaseline
  color: string | CanvasGradient | CanvasPattern
  fontFamily: string
  fontWeight: FontWeightVars
  fontStyle: FontStyleVars
  fillStyle: string | CanvasGradient | CanvasPattern
  backgroundColor?: string | CanvasGradient | CanvasPattern
  textMetrics?: TextMetrics
  wrap?: boolean
  contents?: string[]
  wordSpacing?: number
  constructor(content: string, options: TextOptions) {
    super(options)
    this.content = content
    this.textAlign = options.textAlign ?? TextAlign.left
    this.fontSize = options.fontSize ?? 12
    this.lineHeight = options.lineHeight ?? 14
    this.baseline = options.baseline ?? 'top'
    this.color = options.color ?? 'black'
    this.fontFamily = options.fontFamily ?? 'sans-serif'
    this.backgroundColor = options.backgroundColor ?? 'transparent'
    this.wrap = options.wrap ?? false
    this.wordSpacing = 0
    this.fontWeight = options.fontWeight ?? 'normal'
    this.fontStyle = options.fontStyle ?? 'normal'
  }

  async draw(context: CanvasRenderingContext2D, dt: number): Promise<void> {
    var x = this.screenX
    var y = this.screenY
    var lineHeight = Math.max(this.lineHeight, this.fontSize)
    var offsetY = lineHeight > this.fontSize ? (lineHeight - this.fontSize) / 2 : 0
  

    context.fillStyle = this.backgroundColor ?? this.color ?? '#fff'
    context.fillRect(x, y, this.width, this.height)

    context.font = `${this.fontWeight!=='normal' ? this.fontWeight: ''} ${this.fontStyle!=='normal' ? this.fontStyle: ''} ${this.fontSize}px ${this.fontFamily}`
    context.textBaseline = this.baseline 
    context.fillStyle = this.color
    context.textAlign = 'left'
    context.wordSpacing = `${this.wordSpacing}px`

    if(this.contents && this.contents.length > 0) {
      for (let index = 0; index < this.contents.length; index++) {
        const content = this.contents[index];
        y = this.screenY + index * this.lineHeight
        var offsetX = this.getLeadingOffset(content)
        context.fillText(content, x + offsetX, y + offsetY, this.width)
      }
    }
  }

  async init(): Promise<void> {
      super.init()
      this.resize()
  }

  async resize(): Promise<void> {
    var context = this.context

    var computeOptions = this.computeContents(context)
    this.height = computeOptions.height
    this.width = computeOptions.width
    this.contents = computeOptions.contents
    super.resize()
  }


  getLeadingOffset(content: string) {
    if(typeof this.options.width === 'number') {
      var textMetrics = this.context.measureText(content)
      var freeSpacing = this.options.width - textMetrics.width
      switch(this.textAlign) {
        case TextAlign.left: return 0
        case TextAlign.center: return freeSpacing / 2
        case TextAlign.right: return freeSpacing
      }
    } else {
      return 0
    }
  }

  computeContents(context: CanvasRenderingContext2D): {
    width: number
    height: number
    contents: string[]
  } {
    var lineHeight = Math.max(this.lineHeight, this.fontSize)
    var maxWidth = this.options.width ?? 0
    if(!this.wrap) {
      var context = this.context
      context.font = `${this.fontSize}px ${this.fontFamily}`
      var ls = this.content.split('\n')
      if(typeof this.options.width != 'number') {
        for(var i =0;i<ls.length;i++) {
          var textMetrics: TextMetrics = context.measureText(ls[i])
          maxWidth = Math.max(textMetrics.width, maxWidth)
        }
      }
      var height = this.options.height
      if(typeof this.options.height != 'number') {
        height = lineHeight * ls.length
      }
      return {
        width: maxWidth,
        height,
        contents: ls
      }
    }

    if(typeof this.options.width !== 'number') {
      throw Error("文本自动换行需要定义文本组件宽度")
    }
    
    let line = '';
    let contents = []
    let lines = this.content.split('\n');
    for(let l = 0; l < lines.length ;l ++) {
      const words = lines[l].split('');
      for (let n = 0; n < words.length; n++) {
          const testLine = line + words[n];
          const metrics = context.measureText(testLine);
          const testWidth = metrics.width;
          if (testWidth > maxWidth && n > 0) {
              contents.push(line);
              line = words[n];
          } else {
              line = testLine;
          }
      }
    }

    contents.push(line);

    return {
      width: maxWidth,
      height: lineHeight * contents.length,
      contents: contents
    }
  }
}